#include "stdafx.h"
#include "net/HTTPCredentials.hpp"
#include "net/HTTPBasicCredentials.hpp"

NET_NAMESPACE_BEGIN

CHTTPCredentials::CHTTPCredentials()
{
}

CHTTPCredentials::CHTTPCredentials(const std::string& username, const std::string& password) : m_digest(username, password)
{
}

CHTTPCredentials::~CHTTPCredentials()
{
}

void CHTTPCredentials::fromUserInfo(const std::string& userInfo)
{
	std::string username;
	std::string password;

	extractCredentials(userInfo, username, password);
	setUsername(username);
	setPassword(password);
	m_digest.reset();
}

void CHTTPCredentials::fromURI(const CURI& uri)
{
	std::string username;
	std::string password;

	extractCredentials(uri, username, password);
	setUsername(username);
	setPassword(password);
	m_digest.reset();
}

void CHTTPCredentials::authenticate(CHTTPRequest& request, const CHTTPResponse& response)
{
	for (CHTTPResponse::ConstIterator iter = response.find(CHTTPAuthenticationParams::WWW_AUTHENTICATE); iter != response.end(); ++iter)
	{
		if (isBasicCredentials(iter->second)) 
		{
			CHTTPBasicCredentials(m_digest.getUsername(), m_digest.getPassword()).authenticate(request);
			return;
		} 
		else if (isDigestCredentials(iter->second)) 
		{
			m_digest.authenticate(request, CHTTPAuthenticationParams(iter->second.substr(7)));
			return;
		}
	}
}

void CHTTPCredentials::updateAuthInfo(CHTTPRequest& request)
{
	if (request.has(CHTTPRequest::AUTHORIZATION)) 
	{
		const std::string& authorization = request.get(CHTTPRequest::AUTHORIZATION);

		if (isBasicCredentials(authorization)) 
		{
			CHTTPBasicCredentials(m_digest.getUsername(), m_digest.getPassword()).authenticate(request);
		} 
		else if (isDigestCredentials(authorization)) 
		{
			m_digest.updateAuthInfo(request);
		}
	}
}

void CHTTPCredentials::proxyAuthenticate(CHTTPRequest& request, const CHTTPResponse& response)
{
	for (CHTTPResponse::ConstIterator iter = response.find(CHTTPAuthenticationParams::PROXY_AUTHENTICATE); iter != response.end(); ++iter)
	{
		if (isBasicCredentials(iter->second)) 
		{
			CHTTPBasicCredentials(m_digest.getUsername(), m_digest.getPassword()).proxyAuthenticate(request);
			return;
		} 
		else if (isDigestCredentials(iter->second)) 
		{
			m_digest.proxyAuthenticate(request, CHTTPAuthenticationParams(iter->second.substr(7)));
			return;
		}
	}
}

void CHTTPCredentials::updateProxyAuthInfo(CHTTPRequest& request)
{
	if (request.has(CHTTPRequest::PROXY_AUTHORIZATION)) 
	{
		const std::string& authorization = request.get(CHTTPRequest::PROXY_AUTHORIZATION);

		if (isBasicCredentials(authorization)) 
		{
			CHTTPBasicCredentials(m_digest.getUsername(), m_digest.getPassword()).proxyAuthenticate(request);
		} 
		else if (isDigestCredentials(authorization)) 
		{
			m_digest.updateProxyAuthInfo(request);
		}
	}
}

bool CHTTPCredentials::isBasicCredentials(const std::string& header)
{
	return icompare(header, 0, 5, "Basic") == 0 && (header.size() > 5 ? CAscii::isSpace(header[5]) : true);
}

bool CHTTPCredentials::isDigestCredentials(const std::string& header)
{
	return icompare(header, 0, 6, "Digest") == 0 && (header.size() > 6 ? CAscii::isSpace(header[6]) : true);
}

bool CHTTPCredentials::hasBasicCredentials(const CHTTPRequest& request)
{
	return request.has(CHTTPRequest::AUTHORIZATION) && isBasicCredentials(request.get(CHTTPRequest::AUTHORIZATION));
}

bool CHTTPCredentials::hasDigestCredentials(const CHTTPRequest& request)
{
	return request.has(CHTTPRequest::AUTHORIZATION) && isDigestCredentials(request.get(CHTTPRequest::AUTHORIZATION));
}

bool CHTTPCredentials::hasProxyBasicCredentials(const CHTTPRequest& request)
{
	return request.has(CHTTPRequest::PROXY_AUTHORIZATION) && isBasicCredentials(request.get(CHTTPRequest::PROXY_AUTHORIZATION));
}

bool CHTTPCredentials::hasProxyDigestCredentials(const CHTTPRequest& request)
{
	return request.has(CHTTPRequest::PROXY_AUTHORIZATION) && isDigestCredentials(request.get(CHTTPRequest::PROXY_AUTHORIZATION));
}

void CHTTPCredentials::extractCredentials(const std::string& userInfo, std::string& username, std::string& password)
{
	const std::string::size_type p = userInfo.find(':');

	if (p != std::string::npos) 
	{
		username.assign(userInfo, 0, p);
		password.assign(userInfo, p + 1, std::string::npos);
	} 
	else 
	{
		username.assign(userInfo);
		password.clear();
	}
}

void CHTTPCredentials::extractCredentials(const CURI& uri, std::string& username, std::string& password)
{
	if (!uri.getUserInfo().empty()) 
	{
		extractCredentials(uri.getUserInfo(), username, password);
	}
}

NET_NAMESPACE_END
